/*
 * @Author: your name
 * @Date: 2021-12-07 19:28:42
 * @LastEditTime: 2021-12-08 10:25:13
 * @LastEditors: Please set LastEditors
 * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 * @FilePath: /vite_vue3_ts/src/utils/http/axios/Axios.ts
 */
import type { AxiosRequestConfig, AxiosInstance, AxiosResponse } from "axios";
import axios from "axios";
import { AxiosCanceler } from "./axiosCancel";
import { isFunction } from "@/utils/is";
import type { RequestOptions, Result, CreateAxiosOptions } from "./types";
import { cloneDeep } from "lodash-es";

/**
 * @description: axios模块
 * @param {*}
 * @return {*}
 */
export class HAxios {
  // axios实例
  private axiosInstance: AxiosInstance;
  private options: CreateAxiosOptions;

  constructor(options: CreateAxiosOptions) {
    this.options = options;
    this.axiosInstance = axios.create(options);
    this.setupInterceptors();
  }

  /**
   * 创建axios实例
   * @param config
   */
  private createAxios(config: CreateAxiosOptions): void {
    this.axiosInstance = axios.create(config);
  }

  private getTransform() {
    const { transform } = this.options;
    return transform;
  }

  /**
   * 获得实例
   * @returns
   */
  getAxios(): AxiosInstance {
    return this.axiosInstance;
  }

  /**
   * 重新配置axios
   * @param config
   */
  configAxios(config: CreateAxiosOptions) {
    if (!this.axiosInstance) {
      return;
    }
    this.createAxios(config);
  }

  /**
   * 设置通用header
   * @param headers
   * @returns
   */
  setHeader(headers: any): void {
    if (!this.axiosInstance) {
      return;
    }
    Object.assign(this.axiosInstance.defaults.headers, headers);
  }

  /**
   * 拦截器配置
   */
  private setupInterceptors() {
    const transform = this.getTransform();
    if (!transform) {
      // 没有则返回
      return;
    }

    const {
      requestInterceptors, //请求拦截
      requestInterceptorsCatch, // 请求错误拦截
      responseInterceptors, // 响应拦截
      responseInterceptorsCatch, // 响应错误拦截
    } = transform;

    const axiosCanceler = new AxiosCanceler();

    // 请求拦截器配置处理
    this.axiosInstance.interceptors.request.use(
      (config: AxiosRequestConfig) => {
        const {
          headers: { ignoreCancelToken } = { ignoreCancelToken: false },
        } = config;
        // 添加
        !ignoreCancelToken && axiosCanceler.addPending(config);
        if (requestInterceptors && isFunction(requestInterceptors)) {
          config = requestInterceptors(config);
        }
        return config;
      },
      undefined
    );

    // 请求拦截器错误捕获
    requestInterceptorsCatch &&
      isFunction(requestInterceptorsCatch) &&
      this.axiosInstance.interceptors.request.use(
        undefined,
        requestInterceptorsCatch
      );

    // 响应结果拦截器处理
    this.axiosInstance.interceptors.response.use((res: AxiosResponse<any>) => {
      // 响应成功，移除
      res && axiosCanceler.removePending(res.config);
      if (responseInterceptors && isFunction(responseInterceptors)) {
        res = responseInterceptors(res);
      }
      return res;
    }, undefined);

    // 响应结果拦截器错误捕获
    responseInterceptorsCatch &&
      isFunction(responseInterceptorsCatch) &&
      this.axiosInstance.interceptors.response.use(
        undefined,
        responseInterceptorsCatch
      );
  }

  /*
   *@Description: 请求方法
   *@Author: huoran
   *@Date: 2021-12-08 10:08:08
   */
  request<T = any>(
    config: AxiosRequestConfig,
    options?: RequestOptions
  ): Promise<T> {
    let conf: AxiosRequestConfig = cloneDeep(config);
    const transform = this.getTransform();

    const { requestOptions } = this.options;
    /*
     *@Description: 
        Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
        Object.assign(target, ...sources)    【target：目标对象】，【souce：源对象（可多个）】
     *@Author: huoran
     *@Date: 2021-12-08 10:16:40
    */
    const opt: RequestOptions = Object.assign({}, requestOptions, options);

    const { beforeRequestHook, requestCatch, transformRequestData } =
      transform || {};
    // 请求之前配置
    if (beforeRequestHook && isFunction(beforeRequestHook)) {
      conf = beforeRequestHook(conf, opt);
    }

    return new Promise((resolve, reject) => {
      this.axiosInstance
        .request<any, AxiosResponse<Result>>(conf)
        .then((res: AxiosResponse<Result>) => {
          // 请求是否被取消
          const isCancel = axios.isCancel(res);
          // 请求成功处理
          if (
            transformRequestData &&
            isFunction(transformRequestData) &&
            !isCancel
          ) {
            const ret = transformRequestData(res, opt);
            return resolve(ret);
          }
          reject(res as unknown as Promise<T>);
        })
        .catch((e: Error) => {
          // 请求错误处理
          if (requestCatch && isFunction(requestCatch)) {
            reject(requestCatch(e));
            return;
          }
          reject(e);
        });
    });
  }

  // /**
  //  * @description:  文件上传
  //  */
  // uploadFiles(config: AxiosRequestConfig, params: File[]) {
  //   const formData = new FormData();

  //   Object.keys(params).forEach((key) => {
  //     formData.append(key, params[key as any]);
  //   });

  //   return this.request({
  //     ...config,
  //     method: 'POST',
  //     data: formData,
  //     headers: {
  //       'Content-type': ContentTypeEnum.FORM_DATA,
  //     },
  //   });
  // }
}
